package org.zhixiang.gisQuery.query;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.zhixiang.gisQuery.rtree.LinePoint;
import org.zhixiang.gisQuery.rtree.Point;
import org.zhixiang.gisQuery.rtree.RTree;
import org.zhixiang.gisQuery.rtree.Rectangle;
import org.zhixiang.gisQuery.utils.AreaNodeRecord;
import org.zhixiang.gisQuery.utils.JuXingMaxRect;

import com.fasterxml.jackson.annotation.JsonIgnore;

import gnu.trove.procedure.TIntProcedure;
import lombok.Data;

/**
 * 
 * @say little Boy, don't be sad.
 * @name Rezar
 * @time 2018年9月13日 下午10:47:08
 * @Desc 些年若许,不负芳华.
 *
 */
@Data
public class RectangleMBR {

	private Integer mbrId;
	private String polylineStr;
	private List<SelfPoint> polyline;
	private Rectangle rectangle;// 外接矩形(平行xy轴)
	private Rectangle minRectangle;// 不规则多边形内面积最大的一块矩形区域
	private Rectangle secondMinRectangle; // 不规则多边形内面积次大的一块矩形区域
	@JsonIgnore
	private Map<Integer, LinePoint> pointCache = new HashMap<>();
	@JsonIgnore
	private RTree lineRtree;

	private String city;
	private String prov;

	private String cityCode;

	public void setPolyline(List<SelfPoint> linePoints) {
		this.lineRtree = new RTree();
		this.lineRtree.init(null);
		int index = 0;
		for (int i = 0, l = linePoints.size(), j = l - 1; i < l; j = i, i++) {
			SelfPoint first = linePoints.get(i);
			SelfPoint second = linePoints.get(j);
			LinePoint linePoint = new LinePoint(first, second);
			double sx = first.x, sy = first.y, tx = second.x, ty = second.y;
			this.lineRtree.add(new Rectangle(sx, sy, tx, ty), index);
			pointCache.put(index, linePoint);
			index++;
		}
		this.polyline = linePoints;
	}

	public void initMinBound(int xCount, int yCount) {
		double blockX = (rectangle.maxX - rectangle.minX) / xCount;
		double blockY = (rectangle.maxY - rectangle.minY) / yCount;
		Integer[][] retBlockDatas = new Integer[xCount][yCount];
		for (int x = 0; x < xCount; x++) {
			for (int y = 0; y < yCount; y++) {
				int checkInRegio = checkInRegio(this.lineRtree, blockX * x, blockY * y, blockX * x + blockX,
						blockY * y + blockY);
				retBlockDatas[x][y] = checkInRegio;
			}
		}
		AreaNodeRecord findMaxArea = JuXingMaxRect.findMaxArea(retBlockDatas);
		this.minRectangle = buildRectangle(blockX, blockY, findMaxArea);
	}

	public static int countOfMulti = 0;

	public void initMinBoundes(int xCount, int yCount) {
		double blockX = (rectangle.maxX - rectangle.minX) / xCount;
		double blockY = (rectangle.maxY - rectangle.minY) / yCount;
		double approximateArea = 0d;
		double eachBlockArea = blockX * blockY;
		Integer[][] retBlockDatas = new Integer[xCount][yCount];
		for (int x = 0; x < xCount; x++) {
			for (int y = 0; y < yCount; y++) {
				int checkInRegio = checkInRegio(this.lineRtree, blockX * x, blockY * y, blockX * x + blockX,
						blockY * y + blockY);
				retBlockDatas[x][y] = checkInRegio;
				if (checkInRegio == 1) {
					approximateArea += eachBlockArea;
				}
			}
		}
		List<AreaNodeRecord> findAllMaxArea = JuXingMaxRect.findAllMaxArea(retBlockDatas);
		if (findAllMaxArea != null && !findAllMaxArea.isEmpty()) {
			for (AreaNodeRecord record : findAllMaxArea) {
				if (record.getArea() == 0.0d) {
					continue;
				}
				Rectangle rectangle = buildRectangle(blockX, blockY, record);
				if (this.checkNotIntersect(rectangle) && needAdd(record.getArea(), approximateArea, 0.3)) {
					if (this.minRectangle == null) {
						this.minRectangle = rectangle;
					} else if (this.secondMinRectangle == null) {
						this.secondMinRectangle = rectangle;
					} else {
						break;
					}
				}
			}
		}
		findAllMaxArea.clear();
		findAllMaxArea = null;
	}

	/**
	 * @param area
	 * @param approximateArea
	 * @param d
	 * @return
	 */
	private boolean needAdd(Double area, double approximateArea, double d) {
		// TODO 判断面积
		return true;
	}

	/**
	 * @param rectangle2
	 * @return
	 */
	private boolean checkNotIntersect(Rectangle rectangle) {
		Rectangle cur = null;
		if (this.minRectangle != null) {
			cur = this.minRectangle;
		}
		return cur != null ? !cur.intersects(rectangle) : true;
	}

	/**
	 * @param blockX
	 * @param blockY
	 * @param findMaxArea
	 * @return
	 */
	private Rectangle buildRectangle(double blockX, double blockY, AreaNodeRecord findMaxArea) {
		int yIndex = findMaxArea.getOriginLevel();
		int xIndex = findMaxArea.getStartIndex() - 1;
		double height = (double) findMaxArea.getHeight() * blockY;
		double minX = rectangle.minX + xIndex * blockX;
		double minY = rectangle.minY + (yIndex * blockY);
		double maxX = minX + findMaxArea.getWidth() * blockX;
		double maxY = minY + height;
		Rectangle rectangle = new Rectangle(minX, minY, maxX, maxY);
		return rectangle;
	}

	public boolean checkInMinRect(Point point) {
		return this.minRectangle != null && this.minRectangle.containPoint(point);
		// return this.minRectangle != null ?
		// this.minRectangle.containPoint(point)
		// || (this.secondMinRectangle != null &&
		// this.secondMinRectangle.containPoint(point)) : false;
	}

	// public Boolean checkInMinRectes(Point point) {
	// if (this.minRectangles != null && !this.minRectangles.isEmpty()) {
	// for (Rectangle cur : this.minRectangles) {
	// if (cur.containPoint(point)) {
	// return true;
	// }
	// }
	// }
	// return null;
	// }

	/**
	 * @param lineRtree2
	 * @param d
	 * @param e
	 * @param f
	 * @param g
	 * @return
	 */
	private int checkInRegio(RTree lineRtree, double minX, double minY, double maxX, double maxY) {
		Point point1 = new Point(this.rectangle.minX + minX, this.rectangle.minY + minY);
		Point point2 = new Point(this.rectangle.minX + minX, this.rectangle.minY + maxY);
		Point point3 = new Point(this.rectangle.minX + maxX, this.rectangle.minY + minY);
		Point point4 = new Point(this.rectangle.minX + maxX, this.rectangle.minY + maxY);
		return this.checkInRange(point1) && this.checkInRange(point2) && this.checkInRange(point3)
				&& this.checkInRange(point4) ? 1 : 0;
	}

	public static int useTime = 0;

	public boolean checkInRange(Point point) {
		Boolean checkInMinRect = this.checkInMinRect(point);
		if (checkInMinRect) {
			return true;
		}
		final ArrayList<LinePoint> checkLines = new ArrayList<>();
		this.lineRtree.nearestForLine(point, new TIntProcedure() {
			@Override
			public boolean execute(int value) {
				checkLines.add(pointCache.get(value));
				return true;
			}
		}, 0.00d);
		boolean checkInBoundWithLine = InBoundChecker.checkInBoundWithLine(checkLines, point.x, point.y);
		return checkInBoundWithLine;
	}

}
